使用Web Worker开启浏览器多线程

  • Sorzen
  • 9 Minutes
  • September 26, 2018

前言

javascript是单线程模型,如果要处理一些密集型任务时可能会拖慢主进程的执行(如页面UI卡顿),是否可以使javascript像其他语言使用多线程方式运行,一部分运行在UI线程下,另一部分运行一个独立线程(如只进行某些复杂运算)。

Web Worker作用就是为javascript提供一个创建多线程环境,但是这是浏览器的功能,实际和javascript语言本身几乎没什么关系,浏览器可以提供多个引擎实例,各自运行在自身的线程上,这样就可以在不同的线程上运行不同的程序。在worker内部是无法访问主程序的任何资源,这意味着你不能访问它的任何全局变量,也不能访问页面的DOM或者其他资源,这是一个完全独立的线程。

Web Worker 有以下几个使用注意点。

(1)同源限制
分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

(2)DOM 限制
Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用document、window、parent这些对象。但是,Worker 线程可以navigator对象和location对象。

(3)通信联系
Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

(4)脚本限制
Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

(5)文件限制
Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

用法

Worker之间以及他们主程序之间,不会共享任何作用域或资源,它是通过一个基本的事件消息机制相互联系。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

/*实例化一个worker*/
var worker = new Worker('URL'); // 这个文件应该指向一个javascript文件,这个文件就是worker线程要执行的任务,但是worker不能读取本地文件,所以这个文件需要来自网络。


// 监听函数,监听'message'事件
worker.addEventListener('message', function(event) {
console.log(event.data);
});


/*向worker传递信息*/
worker.postMessge('hello worker');


/*从worker接收信息,主线程接收子线程传来的消息*/
worker.onmessage = function(event){
console.log(event.data) //'hello worker'
}


// 在worker中加载额外的javascri脚本
importScripts("url"); // 脚本记载是同步的,会阻塞余下worker的执行


// 终止主线程
worker.terminate(); // 突然终止线程不会给他任何机会完成他的工作或者清理任何资源,这就类似于直接关闭浏览器标签页。

具体使用详情可参考:Web Worker 使用教程

应用场景

Web Worker通常应用于哪些方面?

实战

当对一个文件需要生成MD5时,一般都是将文件传给后台然后将结果传递给前台进行使用,是否可以在前台直接生成然后使用呢?答案是肯定的,但是在未使用Web Worker时可能会影响页面响应,导致较大的文件不太使用此方法,但是我们可以用这个想法尝试使用一下Web Worker。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
const file = files[0]; // 获取文件对象

const fileReader = new FileReader();
const worker = new Worker('readFileAsBuffer.js') // 开启worker线

/*fileReader读取完成,传递给worker*/
fileReader.onload = function(e){
worker.postMessage({
operation:'sendArrayBuffer',
input:e.target.result,
threshold:0.8,
finish:true
},[e.target.result])
}

fileReader.onerror = function(){
console.log('文件读取失败,请重试');
}

/*worker线程计算MD5完成并返回结果*/
worker.onmessage = function(event){
/*读取完成,获取md5*/
const md5 = event.data;
file.md5 = md5;
}

/*使用FileReader读取文件*/
fileReader.readAsArrayBuffer(blobSlice.call(file,0,file.size));


//-----woker文件部分,readFileAsBuffer.js-------

/*在worker线程中,如果需要引入别的文件,要使用importScripts*/
importScripts('@/node_modules/spark-md5/spark-md5.js');


const spark = new SparkMD5.ArrayBuffer();
const saveArrayBuffer = [];
const start = false;
const finish = false;


/*接收到主线程发来的文件*/
onmessage = function(event){
spark.append(event.data.input);
const md5 = spark.end();
postMessage(md5);
}

目前对于Web Worker用得还比较少,也希望以后能看到全面且深度利用Web Worker的有意思的开源框架或项目。